蔡比八寫後端(6) - TypeORM entity & column


Posted by TempuraEngineer on 2023-12-19

目錄


columns

entity的column對應的就是table的column,定義方式有裝飾器(下方使用)、schema 2種

  • entity column

    • @Column
    • 對應table中的一般column
  • primary columns

    • primary column是被設為primary key的column,每個entity都至少一個primary column

          -- 幫已存在的column新增約束
          -- uniqueLibraryName是約束的名稱,可隨意取
          ALTER TABLE libraries
          ADD CONSTRAINT uniqueLibraryName PRIMARY (name);
      
    • @PrimaryColumn()

      • primary key的column
    • @PrimaryGeneratedColumn()
      • primary key + auto-increment的column
  • special column

  • enum column

MySQL支援enum column,可以用在狀態、類型這類有選項的地方

當試著新增一筆資料時,若enum column輸入的值不在enum內,就會得到"Data truncated for column '欄位名城' at row n"的報錯

-- 修改已存在的column型別為enum
ALTER TABLE books
MODIFY status ENUM('ready', 'renting', 'missing');
  @Column("enum", {
    name: "status",
    nullable: true,
    enum: ["ready", "renting", "missing"],
  })
  status: "ready" | "renting" | "missing" | null;

其他還有spatial column(記錄位置資料)、set column(MySQL不支援)、simple-json(在前一篇示範過了,JSON column就是垃圾)之類的,但就不多介紹了


entity繼承 & 嵌入entity

這兩種方式都可以減少重複的column,但還是不一樣

繼承用在column名稱完全不一樣時(ex: name, phone),嵌入則用在前綴一樣時(ex: nameFirst, nameLast)


繼承

假設publisher、user都有phone、name這兩個欄位

先建立一個擁有共同的column的class

export abstract class FixedInfo {
  @Column("varchar", { name: "name", length: 100 })
  name: string;

  @Column("varchar", { name: "phone", nullable: true, length: 10 })
  phone: string | null;
}

然後extends

@Entity("publishers", { schema: "bookInfos" })
export class Publishers {
  @PrimaryGeneratedColumn({ type: "int", name: "id" })
  id: number;

  @OneToMany(() => Books, (books) => books.publisher)
  books: Books[];
}


export class Users extends FixedInfo {
  @PrimaryGeneratedColumn({ type: "int", name: "id" })
  id: number;

  @Column("int", { name: "neglect", default: () => "'0'" })
  neglect: number;

  @OneToMany(() => Books, (books) => books.rentTo2)
  books: Books[];
}


嵌入

先建立一個擁有共同的column的class

export abstract class FixedInfo {
  @Column("varchar", { name: "name", length: 100 })
  name: string;

  @Column("varchar", { name: "phone", nullable: true, length: 10 })
  phone: string | null;
}

然後connect

注意,如果column名完全不一樣,千萬不要用這個方式,不只得不到要的資料,還會把你的table搞得一團亂

得不到要的資料
table被搞得一團亂

如果只是前綴不一樣記得要設定 {prefix:false},設定完後這個column會被當成是底下有name和phone

import { FixedInfo } from "./FixedInfo"

export class Users {
  @PrimaryGeneratedColumn({ type: "int", name: "id" })
  id: number;

  // 有設定prefix: false的話,就會當成是name這個欄位底下有name和phone
  @Column(() => FixedInfo, {prefix:false})
  name: FixedInfo;

  @Column("int", { name: "neglect", default: () => "'0'" })
  neglect: number;

  @OneToMany(() => Books, (books) => books.rentTo2)
  books: Books[];
}


view & view entity

view

view是MySQL query的結果(檢視表),長得就像table,但真的存在於資料庫

真的存在資料庫的是Materialized View。它可以用來做暫存,但到目前的MySQL 8.0都不支援(雖然有方法可以做出類似的效果)

使用view的優點如下

  1. 簡化查詢複雜度,加快開發速度(ex: 每週都會撈每個圖書館的借閱統計,只要做好view就不用每次都打重複的sql語法了)

     CREATE VIEW view AS (SELECT books.id, books.name
     FROM books);
    
  2. 加強資料庫的安全性(可以根據不同權限給予看到的view)

  3. 資料表變更結構時只需更改 view 的語法

       ALTER VIEW view (id, bookName, price) AS 
       (SELECT id, name, price
       FROM books 
       ORDER BY price DESC);
    

但是view也是有缺點的

  1. 不是所有view都可以update。涉及多個table、有子查詢、有用到聚合函數、distinct、group、having、union的都無法update,因為它們屬於TEMPTABLE演算法
    • TEMPTABLE演算法是建立view時的三個可選演算法之一,預設是UNDEFINED。當設為UNDEFINED時MySQL會自己選要用TEMPTABLE或MERGE
  2. view本身沒有資料,因此所有對view進行資料操作都會體現在原table中,所以千萬不要隨意update view


view entity

view entity是一個class,它對應DB的view,當DB裏沒有對應的view,就會自己建立一個

可以直接寫SQL語法

@ViewEntity({
    expression: `
        SELECT "post"."id" AS "id", "post"."name" AS "name", "category"."name" AS "categoryName"
        FROM "post" "post"
        LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
    `
})

也可以使用TypeORM的抽象工具

@ViewEntity({
    expression: (dataSource: DataSource) => dataSource
        .createQueryBuilder()
        .select("post.id", "id")
        .addSelect("post.name", "name")
        .addSelect("category.name", "categoryName")
        .from(Post, "post")
        .leftJoin(Category, "category", "category.id = post.categoryId")
})

但是TypeORM是無法直接query view的

How to query a view instead of a table?


參考資料

Embedded Entities
Entity Inheritance
提升服務效能、減輕DB負擔!(2): Materialized View
View 視圖


#TypeORM #View #column #Entity







Related Posts

出發點

出發點

LeetCode JS Easy 2704. To Be Or Not To Be

LeetCode JS Easy 2704. To Be Or Not To Be

滲透測試基本技術 第三章 (001)

滲透測試基本技術 第三章 (001)


Comments